<?php

//<editor-fold defaultstate="collapsed" desc="License & Copyright">
/*
 * @license
 */

/**
 *
 * @author Jevgeni Geimanen (jevgeni.geimanen@gmail.com)
 */
//</editor-fold>

abstract class SCSite extends Container {

    //<editor-fold defaultstate="collapsed" desc="Instance">
    /**
     *
     * @var Site
     */
    private static $_Instance = null;

    /**
     *
     * @return Site
     */
    public static function getInstance() {
        if (self::$_Instance === null) {
            self::$_Instance = new Site();
            if (is_array($arr = SCCacheHelper::getInstance()->getObject('SCComponent_Labels')))
                SCComponent::$_Labels = array_merge($arr, SCComponent::$_Labels);
        }
        return self::$_Instance;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Construct & Overrides">
    public function __construct() {
        if (self::$_Instance !== null)
            throw new SingletonException();
        self::$_Instance = $this;
        parent::__construct();
    }

    public function Create() {
        $this->getCurrentPage();
        return parent::Create();
    }

    public function getControlName() {
        return "Site";
    }

    /**
     *
     * @return Site
     */
    public function CollectJSProperties(&$collection) {
        $collection['postback_url_preffix'] = $this->getUrlPreffix();
        return parent::CollectJSProperties($collection);
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Fields">
    //<editor-fold defaultstate="collapsed" desc="   CurrentPage">
    /**
     * @var Page
     */
    private $_CurrentPage;

    /**
     * @TODO improve default page searching logic
     * @TODO Configure
     *  - "p" string
     *  - "default" string
     *  - "404" string
     * @return type
     */
    public function getCurrentPage() {
        if ($this->_CurrentPage === null) {
            if (count($pages = $this->GetAllPages()) > 0 && isset($pages[$p = $this->getFdatValue('c')]) || isset($pages[$p = 'default'])) {
                $this->getChildControls()->Add($this->_CurrentPage = new $pages[$p]());
            } else {
                print "No Page";
                exit;
            }
        }
        return $this->_CurrentPage;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   UrlPreffix">
    private $_UrlPreffix = '';

    /**
     *
     * @return SCSite
     */
    public function setUrlPreffix($preffix) {
        $this->_UrlPreffix = '/' . $preffix;
        return $this;
    }

    /**
     *
     * @return string
     */
    public function getUrlPreffix() {
        return $this->_UrlPreffix;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   Fdat">
    private static $_Fdat = null;

    public static function getFdat() {
        if (self::$_Fdat === null) {
            switch ($_SERVER['REQUEST_METHOD']) {
                case 'POST':
                    self::$_Fdat = array_merge($_GET, $_POST);
                case 'GET':
                    self::$_Fdat = array_merge($_POST, $_GET);
                    break;
            }
        }
        return self::$_Fdat;
    }

    //</editor-fold>
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Public Methods">
    //<editor-fold defaultstate="collapsed" desc="   getFdatValue">
    public static function getFdatValue($name) {
        $fdat = self::getFdat();
        if (isset($fdat[$name])) {
            return $fdat[$name];
        }
        return null;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   getPageResourcesLink">
    private $_PageResourcesLink = array();

    public function getPageResourcesLink($name) {
        if (!isset($this->_PageResourcesLink[$name])) {
            $md5 = md5($name . "-" . join('|', array_keys($this->_registeredComponents)));
            if (!file_exists($filename = SCORE_APP_VAR_PATH . "__page_resources.{$md5}") || IS_DEV) {
                $resources = array(
                    'paths' => array(),
                    'static_vars' => array()
                );
                switch ($name) {
                    case 'js':
                    case 'css':
                        foreach ($this->_registeredComponents as $key => $val) {
                            $lresources = array();
                            $ref = new ReflectionClass($key);
                            $matches = array();
                            while ($ref != null) {
                                if (($fileName = sCore::getRefClassFileName($ref)) != null) {

                                    if (
                                            file_exists($rfilename = dirname($fileName) . "/{$ref->getName()}.$name") ||
                                            $name == 'css' && file_exists($rfilename = dirname($fileName) . "/{$ref->getName()}.less")
                                    ) {
                                        $lresources[] = $rfilename;
                                    }
                                    if ($name == 'js') {
                                        if (SCCoreHelper::ImplementsInterfaceRecursively($ref, 'IJSStruct')) {
                                            $staticVars = array();
                                            if (preg_match("/\@js-enum\s+(?<preffix>[^\s]+)/", $ref->getDocComment(), $matches)) {
                                                $constants = $ref->getConstants();
                                                $preffix = $matches['preffix'];
                                                foreach ($constants as $cName => $cValue)
                                                    if (preg_match("/^{$preffix}/", $cName))
                                                        $staticVars[$cName] = $cValue;
                                            }
                                            if (count($staticVars) > 0)
                                                $resources['static_vars'][$ref->getName()] = $staticVars;
                                        }
                                    }
                                }
                                $ref = $ref->getParentClass();
                            }
                            $lresources = array_reverse($lresources);
                            foreach ($lresources as $kkey)
                                if (!isset($resources['paths'][$kkey]))
                                    $resources['paths'][$kkey] = 1;
                        }
                        break;
                    default:
                        throw new BadMethodCallException();
                }
                file_put_contents($filename, serialize($resources));
            }
            $this->_PageResourcesLink[$name] = "{$this->getUrlPreffix()}/index.php?__page_resources={$name}&md5=$md5";
        }
        return $this->_PageResourcesLink[$name];
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   ProcessRequest">
    /**
     *
     * @return Site
     */
    public function ProcessRequest() {
        switch ($name = $this->getRequestType()) {
            case 'css':
            case 'js':
                $this->ProcessResourceRequest($name);
                break;
            case 'ajax':
                $this
                        ->Init()
                        ->Create()
                        ->DispatchEvents();
                break;
            case 'page':
                echo $this
                        ->Init()
                        ->Create()
                        ->DispatchEvents()
                        ->Render();
                break;
            default:
                echo '500';
                break;
        }
        return $this;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   ProcessResourceRequest">
    private function ProcessResourceRequest($name) {
        //@TODO Add md5 validation
        $md5 = self::getFdatValue('md5');
        header("Etag: {$md5}");
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $md5 && false) {
            header("HTTP/1.1 304 Not Modified");
            header('Content-Length: 0');
        } else {
            if (
                    function_exists("gzencode") &&
                    ($useGzip = isset($_SERVER["HTTP_ACCEPT_ENCODING"]) && (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") > -1))
            ) {
                $encoding = "gzip";
            } else {
                $useGzip = false;
                $encoding = "plain";
            }
            if (!file_exists($filename1 = SCORE_APP_VAR_PATH . "__page_resources.{$md5}.{$encoding}.{$name}") || IS_DEV) {
                if (file_exists($filename = SCORE_APP_VAR_PATH . "__page_resources.{$md5}")) {

                    if (is_array($resources = unserialize(file_get_contents($filename)))) {

                        $res = array();
                        $paths = $resources['paths'];
                        $staticVars = $resources['static_vars'];
                        switch ($name) {
                            case 'js':
                                $paths = array_merge(sCore::getConfig()->getJsConfig()->getInclude(), $paths);
                                foreach ($paths as $path => $temp)
                                    $res[] = file_exists($path) ? file_get_contents($path) : "alert('" . basename($path) . " not found for $name inclusion');";

                                //Applies static vars, like EVENT_* constants
                                foreach ($staticVars as $cName => $cVars) {
                                    $cVarsJson = json_encode($cVars);
                                    $res[] = "$.extend({$cName}.prototype,{$cVarsJson});";
                                }
                                break;
                            case 'css':
                                $cssConfig = sCore::getConfig()->getCssConfig();
                                $paths = array_merge($cssConfig->getInclude(), $paths);
                                $less = array();
                                if (($theme = $cssConfig->getTheme()) != null)
                                    $paths[SCORE_PATH . "css/themes/{$theme}/theme.css"] = 1;
                                foreach ($paths as $path => $temp)
                                    if (file_exists($path)) {
                                        $cc = file_get_contents($path);

                                        /* Replace CSS Vars if any */
                                        if (is_array($vars = $cssConfig->getVar()) && count($vars) > 0)
                                            foreach ($vars as $var => $val)
                                                $cc = preg_replace("/%{$var}%/", $val, $cc);

                                        /* Collect LESS definitions into different array */
                                        preg_match("/\.less$/", $path) ? $less[] = $cc : $res[] = $cc;
                                    }

                                /* If any LESS definitions is collected, parse them */
                                if (count($less) > 0) {
                                    $lessc = new lessc();
                                    $res[] = $lessc->parse(join("\n", $less));
                                }
                                break;
                        }
                        /* IF any results collected, join, gzip and save to cache file */
                        if (count($res) > 0) {
                            $content = join("\n", $res);
                            if ($useGzip)
                                $content = gzencode($content);
                            file_put_contents($filename1, $content);
                        }
                    }
                }
            }
            switch ($name) {
                case "js":
                    header("Content-Type: text/javascript");
                    break;
                case 'css':
                    header("Content-Type: text/css");
                    break;
            }
            header("Content-Encoding: {$encoding}");
            header("Cache-Control: must-revalidate");
            header("Expires: " . gmdate("D, d M Y H:i:s", time() + 2592000) . " GMT");
            if (file_exists($filename1))
                echo readfile($filename1);
        }
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   RegisterComponent">
    private $_registeredComponents = array();

    /**
     *
     * @param Component $component
     * @return Site
     */
    public function RegisterComponent($component) {
        $this->_registeredComponents[$component->getClassName()] = 1;
        return $this;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="   RenderJSDefinition">
    public function RenderJSDefinition() {
        return "sCore.site = new Site(" . json_encode($this->getJSProperties()) . ");";
    }

    //</editor-fold>
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Protected Methods">

    protected static function getRequestType() {
        if (($val = self::getFdatValue('__page_resources')) != null) {
            return $val;
        } else if (self::getFdatValue('ajax') == 1) {
            return 'ajax';
        }
        return 'page';
    }

    //</editor-fold>

    /* @TODO Move that out from here, add cache */

    private $_AllPages = null;

    protected function GetAllPages() {
        if ($this->_AllPages === null) {
            $this->_AllPAges = array();
            if (file_exists($pages_path = SCORE_APP_PATH . "lib/php/web/pages/")) {
                if (count($files = scandir($pages_path)) > 0) {
                    foreach ($files as $file) {
                        if (strlen($file) >= 5 && class_exists($file)) {

                            $classRef = new ReflectionClass($file);
                            $matches = array();
                            if (preg_match("/@page\s+([^\s].*?)[\s$]/", $classRef->getDocComment(), $matches)) {
                                $this->_AllPages[$matches[1]] = $file;
                            }
                            if (preg_match("/@default-page/", $classRef->getDocComment())) {
                                $this->_AllPages['default'] = $file;
                            }
                        }
                    }
                }
            }
        }
        return $this->_AllPages;
    }

    /**
     *
     * @return Site
     */
    public static function factory() {
        return new Site();
    }

}

?>
